package com.jiangyao.common.utils;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.util.concurrent.ListenableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * google本地缓存工具
 *
 * @author: yangyongchuan
 * @create: 2020年05月05日 16:22
 */
public class GuavaCacheUtil {

	private static final Logger LOGGER = LoggerFactory.getLogger(GuavaCacheUtil.class);
	// 使用google guava缓存处理
	private static LoadingCache<String, String> cache;

	static {
		cache = CacheBuilder.newBuilder()
				// 当cache中元素个数超过1000时，则使用LRU进行内存回收
				.maximumSize(1000)
				// 设置cache基于比重大小来清理，这个和maximumSize不可同时使用！！！
				// .maximumWeight(1000)
				// 如果使用基于比重的清理策略，最好自己实现weigher，来指定某些k/v占的比重
				// .weigher(new Weigher<String, String>() {
				//     @Override
				//     public int weigh(String key, String value) {
				//         return 1;
				//     }
				// })
				// 缓存项在给定时间内没有被访问（读/写操作），则回收这个数据项占用内存
				.expireAfterAccess(10, TimeUnit.MINUTES)
				// 缓存项在给定时间内没有被进行写操作，则回收这个数据项占用内存
				//.expireAfterWrite(10, TimeUnit.MINUTES)
				// 缓存项在给的时间内没有进行写操作（创建/更新值），则刷新缓存， 调用reload()去重新加载数据
				//.refreshAfterWrite(10, TimeUnit.MINUTES)
				// 删除监听器，当缓存被删除时，会触发onRemoval()方法
				// RemovalNotification是删除通知，其中包含移除原因[RemovalCause]、键和值。
				.removalListener((RemovalListener<String, String>) notification -> LOGGER.info("onRemoval execute: key=" + notification.getKey() + ",value=" + notification.getValue() + " was deleted，cause=" + notification.getCause().name()))
				/**
				 * recordStats用来开启Guava Cache的统计功能，用于统计缓存命中率、命中次数等值。
				 * 统计打开后，使用Cache.stats()方法会返回CacheStats对象以提供如下统计信息：
				 * hitRate()：缓存命中率；
				 * averageLoadPenalty()：加载新值的平均时间，单位为纳秒；
				 * evictionCount()：缓存项被回收的总数，不包括显式清除。
				 */
				//.recordStats()
				.build(
						// CacheLoader用于处理load, reload等逻辑
						new CacheLoader<String, String>() {
							public String load(String key) {
								LOGGER.info("load .......");
								return key + new Date().toString();
							}

							//重载CacheLoader.reload(K, V)可以扩展刷新时（调用Cache#refresh()方法时）的行为，
							// 这个方法允许在获取新值时返回旧的值。
							@Override
							public ListenableFuture<String> reload(String key, String oldValue) throws Exception {
								LOGGER.info("reload cache:  key=" + key + ",oldValue=" + oldValue);
								return super.reload(key, oldValue);
							}
						});
	}


	/**
	 * 获取缓存
	 *
	 * @param key 缓存key
	 * @return 缓存值
	 */
	public static String get_str(String key) {
		return StringUtils.isNotEmpty(key) ? cache.getIfPresent(key) : null;
	}

	/**
	 * 获取缓存
	 *
	 * @param key 缓存key
	 * @return 缓存值
	 */
	public static <T> T get(String key, Class<T> cls) {
		return StringUtils.isNotEmpty(key) ? JsonUtil.parse(cache.getIfPresent(key), cls) : null;
	}

	/**
	 * 放入缓存
	 *
	 * @param key   缓存key
	 * @param value 缓存值
	 */
	public static void put_str(String key, String value) {
		if (StringUtils.isNotEmpty(key) && value != null) {
			cache.put(key, value);
		}
	}

	/**
	 * 放入缓存
	 *
	 * @param key   缓存key
	 * @param value 缓存值
	 */
	public static void put(String key, Object value) {
		if (StringUtils.isNotEmpty(key) && value != null) {
			cache.put(key, JsonUtil.toJSONString(value));
		}
	}


	/**
	 * 移除缓存
	 *
	 * @param key 缓存key
	 */
	public static void remove(String key) {
		if (StringUtils.isNotEmpty(key)) {
			cache.invalidate(key);
		}
	}

	/**
	 * 批量删除缓存
	 *
	 * @param keys 批量keys
	 */
	public static void remove(List<String> keys) {
		if (keys != null && keys.size() > 0) {
			cache.invalidateAll(keys);
		}
	}


	public static void main(String[] args) throws InterruptedException {
		GuavaCacheUtil.put_str("aaaa", "aaaa1111111111a");
		LOGGER.info("-------:{}", GuavaCacheUtil.get_str("aaaa"));
		for (int i = 0; i < 10; i++) {
			GuavaCacheUtil.put_str("" + i, i + "_aaaa1111111111a");
		}
		Thread.sleep(10000);
		LOGGER.info("-------:{}", GuavaCacheUtil.get_str("aaaa"));
	}
}
